home *** CD-ROM | disk | FTP | other *** search
- #
- # This file is part of OpenVIP (http://openvip.sourceforge.net)
- #
- # Copyright (C) 2002-2003
- # Michal Dvorak, Jiri Sedlar, Antonin Slavik, Vaclav Slavik, Jozef Smizansky
- #
- # This program is licensed under GNU General Public License version 2;
- # see file COPYING in the top level directory for details.
- #
- # $Id: model.py,v 1.29 2003/06/04 12:26:20 xjozo Exp $
- #
- # Timeline data model for the editor. Class Timeline holds all information
- # about edited timeline file and 1-1 corresponds to XML file in OpenVIP's
- # "Timeline Format".
- #
-
-
-
- class Error(Exception):
- """Exception raised when loading/saving/converting model fails.
- self.msg contains error description."""
- def __init__(self, msg):
- self.msg = msg
- def __str__(self):
- return self.msg
-
- # -------------------------------------------------------------------------
- # Timeline objects:
- # -------------------------------------------------------------------------
-
- # NB: Note that most classes have attributes described only in their docstring,
- # but not initialized in constructor. This is on purpose: like this,
- # Python will raise AttributeError when accessing attributes that are not
- # properly filled in.
-
- class Filter:
- """
- Filter (see openvip::VideoFilter, openvip::AudioFilter) applied to Object.
-
- Attributes:
- name - name of the filter plugin to apply
- params - parameters for the filter plugin. It is a dictionary with
- string keys and string values and corresponds to parameters
- accepted by the plugin implementation.
- Assign values like this:
- f.params["width"] = "320"
- f.params["height"] = "240"
- active - a boolean value to quickly turn on/off this filter
- (corresponds to state of CheckListBox in ObjectPanel)
- """
- def __init__(self):
- self.name = None
- self.params = {}
- self.active = True
-
-
- class Group:
- """
- Group object represents group of binded Objects and/or
- Transitions. Objects in group move together when any member of the
- group is moved on the timeline in the editor.
-
- Attributes:
- objects - list of Object or Transition references
- id - string with unique ID of the group
- """
- def __init__(self):
- self.objects = []
-
-
- class SomethingOnTimeline:
- def length(self):
- """Returns length of the object in seconds."""
- return self.time_to - self.time_from
-
- def move(self, pos):
- """Moves the object to new position (in seconds)."""
- len = self.length()
- self.time_from = pos
- self.time_to = pos + len
-
- def isAudio(self):
- return self.track[0] == 'A'
- def isVideo(self):
- return self.track[0] == 'V'
-
-
- class Object(SomethingOnTimeline):
- """
- Object is single video/audio clip pasted on the timeline.
-
- Attributes:
- id - string with unique ID of the object
- time_from - position of the object on the timeline, in seconds
- since beginning
- time_to - position of end of the object on the timeline
- (time_to-time_from gives object's length in seconds)
- track - string identifying the track that this object is placed on.
- Valid values match regex [AV]([AB]|[0-9]+)
- src_spec - specification of the source. This is dictionary with
- string keys and string values and corresponds to parameters
- accepted by corresponding input plugin.
- Note: There MUST be "filename" key in the dictionary! Example: myobj.src_spec["filename"] = "foo.avi"
-
- src_from - position of the object in the source file, in seconds
- since beginning
- src_to - position of end of the object in the source file
- src_to = 0 means end of the source
- src_channel
- - string identification of input track to use. Corresponds
- to output connectors in terminology of OpenVIP core and
- is usually 'video0' or 'audio0'.
- filters - list of Filter object references
- """
- def __init__(self):
- self.src_spec = {}
- self.filters = []
-
-
- class Transition(SomethingOnTimeline):
- """
- Transition object represents transition between two audio or video track
- (you wouldn't guess).
-
- Attributes:
- id - string with unique ID of the transition
- time_from - position of the object on the timeline, in seconds
- since beginning
- time_to - position of end of the object on the timeline
- (time_to-time_from gives object's length in seconds)
- track - track to apply the transition to. Either 'VFx' or 'AFx'
- direction - direction of the effect, either 'AB' or 'BA'
- name - name of the transition plugin to apply
- params - parameters for the transition plugin, see Filter.params
- for more information
- """
- def __init__(self):
- self.params = {}
-
-
- class Timeline:
- """
- Representation of all data contained in edited timeline.
-
- Attributes:
- objects - list of Object references
- transitions - list of Transition references
- groups - list of Group references
- """
- def __init__(self):
- self.objects = []
- self.transitions = []
- self.groups = []
-
- def toxml(self):
- """Converts the object into XML representation."""
- import xml.dom.minidom, conv
- dom = conv.data_to_dom(self,
- "http://openvip.sourceforge.net/timeline-format","1.0")
- s = dom.toxml()
- dom.unlink()
- return s
-
- def tonetworkxml(self, filename, videoFormat, audioFormat,
- time_from=None, time_to=None, previewQuality=False,
- out_params={}):
- """Converts the object into XML network format representation.
- time_from, time_to - convert only part of the timeline
- filename - name of output file created by the network.
- May be None to produce network that renders
- into memory buffer
- output_params - dictionary with additional
- """
- import xml.dom.minidom, conv2
- if time_from == None:
- time_from = self.beginning()
- if time_to == None:
- time_to = self.length()
- if previewQuality:
- quality = "preview"
- else:
- quality = "final"
- out_params['filename']=filename
- return conv2.data_to_network(data=self,
- xmlns="http://openvip.sourceforge.net/network-format",
- version="1.0",
- def_video=videoFormat, def_audio=audioFormat,
- quality=quality,
- output_params=out_params,
- tmln_length=time_to-time_from, tmln_start=time_from)
-
- def save(self, filename):
- """Saves the object to file."""
- f = open(filename, 'wt')
- f.write(self.toxml())
- f.close()
- import xmlhelpers
- xmlhelpers.formatNicely(filename)
-
- def length(self):
- """Returns length of the timeline in seconds -- that is, the distance
- between time 0 and right part of the right-most object."""
- w = 0
- for o in self.objects:
- if o.time_to > w: w = o.time_to
- return w
-
- def beginning(self):
- """Returns time of beginning of the timeline in seconds."""
- if len(self.objects) == 0: return 0
- w = self.objects[0].time_from
- for o in self.objects:
- if o.time_from < w: w = o.time_from
- return w
-
- def cut(self,time,selection = []):
- """Cuts entire Timline in time position, i.e. each object which
- includes time position is cuted and each involved group is devided
- and its objects are redistributed between two devoloped groups."""
- import copy
- groups_to_add = []
- objects_to_remove = []
- for gr in self.groups:
- if toCut(gr,time):
- add_gr = Group()
- add_gr.id = getUniqueID(self,"group",groups_to_add)
- for o in gr.objects:
- if selection != [] and o not in selection:continue
- if toCut(o,time):
- addobj = copy.deepcopy(o)
- addobj.time_to = time
- o.time_from = time
-
- if isinstance(o,Object):
- addobj.src_to = addobj.src_from + (time - addobj.time_from)
- o.src_from = addobj.src_from + (time - addobj.time_from)
- addobj.id = getUniqueID(self,"object")
- if isinstance(o,Transition):addobj.id = getUniqueID(self,"trans")
- add_gr.objects.append(addobj)
- if isinstance(o,Object):self.objects.append(addobj)
- if isinstance(o,Transition):self.transitions.append(addobj)
- else:
- if o.time_to <= time:
- add_gr.objects.append(o)
- objects_to_remove.append(o)
- for ro in objects_to_remove:gr.objects.remove(ro)
- if len(add_gr.objects) > 0:groups_to_add.append(add_gr)
- self.groups.extend(groups_to_add)
-
- obj_to_add = []
- for o in self.objects:
- if selection != [] and o not in selection:continue
- if toCut(o,time):
- addobj = copy.deepcopy(o)
- addobj.time_to = time
- addobj.src_to = addobj.src_from + (time - addobj.time_from)
- o.time_from = time
- o.src_from = addobj.src_from + (time - addobj.time_from)
- addobj.id = getUniqueID(self,"object",obj_to_add)
- obj_to_add.append(addobj)
- self.objects.extend(obj_to_add)
-
- trans_to_add = []
- for o in self.transitions:
- if selection != [] and o not in selection:continue
- if toCut(o,time):
- addobj = copy.deepcopy(o)
- addobj.time_to = time
- o.time_from = time
- addobj.id = getUniqueID(self,"trans",trans_to_add)
- trans_to_add.append(addobj)
- self.transitions.extend(trans_to_add)
-
-
- def getUniqueID(t,root,added = []):
- i=0
- while(1):
- id = "%s%d"%(root,i)
- if inTmln(id,t,added)==0:break
- i+=1
- return id
-
- def inTmln(id,t,added):
- for o in t.objects:
- if o.id == id:return 1
- for tr in t.transitions:
- if tr.id == id:return 1
- for gr in t.groups:
- if gr.id == id:return 1
- for o in added:
- if o.id == id:return 1
- return 0
-
-
-
- def toCut(o,time):
- if isinstance(o,Group):
- for obj in o.objects:
- if obj.time_from < time and obj.time_to > time:
- return 1
- if isinstance(o,Object) or isinstance(o,Transition):
- if o.time_from < time and o.time_to > time:
- return 1
-
- return 0
-
-
- def load(filename):
- """Loads Timeline object from XML file.
- Raises model.Error in case of failure."""
- import xml.dom.minidom, conv, xmlhelpers
- if not xmlhelpers.validate(filename):
- raise Error("File '%s' doesn't conform to DTD." % filename)
- dom = xml.dom.minidom.parse(filename)
- data = conv.dom_to_data(dom)
- dom.unlink()
- return data
-
-
-
- # -------------------------------------------------------------------------
- # Classes for storing information about output stream format:
- # -------------------------------------------------------------------------
-
- class VideoFormat:
- """Description of output format for video track."""
- def __init__(self,i):
- "i is VideoStreamInfo object"
- self.width = i.width
- self.height = i.height
- self.fps = i.fps
- self.aspect_ratio = i.aspect_ratio
- def __init__(self,width,height,fps,aspect_ratio=None):
- self.width = width
- self.height = height
- self.fps = fps
- if aspect_ratio == None:
- self.aspect_ratio = float(width)/float(height)
- else:
- self.aspect_ratio = aspect_ratio
-
- class AudioFormat:
- """Description of output format for audio track."""
- def __init__(self,i):
- "i is AudioStreamInfo object"
- self.sample_rate = i.sample_rate
- self.channels = i.channels
- def __init__(self,sample_rate,channels):
- self.sample_rate = sample_rate
- self.channels = channels
-
-